package com.realife.eson.common.util;

import com.realife.eson.common.asserts.AssertNP;
import com.realife.eson.common.dto.gitlab.FileDir;
import lombok.extern.slf4j.Slf4j;
import org.gitlab4j.api.*;
import org.gitlab4j.api.models.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static cn.hutool.core.codec.Base64.decode;

/**
 * @version V1.0
 * @author: guangcheng
 * @date: 2022/6/3 15:16
 * @description:
 */
@Slf4j
@Component
@SuppressWarnings("all")
public class GitLabUtil {
    /**
     * 文件夹定义: key
     * <p>
     * pathMap.put("group", "helmgroup");
     * pathMap.put("gitBaseUrl", metaSystemItem.getSystemGitBase());
     * pathMap.put("sys", metaSystemItem.getSystemName().toLowerCase());
     * pathMap.put("logicApp", logicAppItem.getAppName().toLowerCase());
     * pathMap.put("env", envItem.getEnvName().toLowerCase());
     * pathMap.put("argoApp", runAppItem.getCdAppName());
     * pathMap.put("templates", "templates");
     */
    public final static String GROUP = "group";
    public final static String GIT_BASE_URL = "gitBaseUrl";
    public final static String SYS = "sys";
    public final static String LOGIC_APP = "logicApp";
    public final static String ENV = "env";
    public final static String ARGO_APP = "argoApp";
    public final static String TEMPLATES = "templates";
    /**
     * 预先设置好根分组
     */
    public final static String ROOT_GROUP = "helmgroup";
    /**
     * 常用文件名
     */
    public final static String VALUES_YAML = "values.yaml";
    public final static String MICROSERVICE_YAML = "microservice.yaml";
    public final static String CHART_YAML = "Chart.yaml";
    public final static String HELM_IGNORE = ".helmignore";
    public final static String HELPERS_TPL = "_helpers.tpl";
    public final static String NOTES_TXT = "NOTES.txt";

    /**
     * 文件类型
     */
    public final static String FILE = "file";
    public final static String DIR = "dir";

    /**
     * helm 资源类型
     */
    public final static String MICRO_SERVICE_TYPE = "Microservice";
    public final static String VALUES_TYPE = "Values";
    public final static String CHART_TYPE = "Chart";
    public final static String HELM_IGNORE_TYPE = "HelmIgnore";
    public final static String HELPERS_TYPE = "Helpers";
    public final static String NOTES_TXT_TYPE = "Notes";


    @Autowired
    GitLabApi gitLabApi;


    /**
     * @param search the project name search criteria
     * @return
     */
    public List<Project> getProjectsByName(String search) {
        ProjectApi projectApi = gitLabApi.getProjectApi();
        try {
            if (AssertNP.legalParams(search)) {
                return projectApi.getProjects(search);
            } else {
                return projectApi.getProjects();
            }
        } catch (GitLabApiException e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        return null;
    }

    /**
     * 创建失败, 发送消息出去, 人工介入检查原因
     *
     * @param projectName 项目名:既系统名: 每个系统对应一个gitBaseUrl
     * @return
     */
    public boolean createProject(String projectName) {
        GroupApi groupApi = gitLabApi.getGroupApi();
        ProjectApi projectApi = gitLabApi.getProjectApi();
        try {
            List<Group> groups = groupApi.getGroups(ROOT_GROUP);
            if (AssertNP.legalParams(groups)) {
                for (Group group : groups) {
                    if (AssertNP.legalParams(group)) {
                        if (AssertNP.equals(ROOT_GROUP, group.getName())) {
                            Project project = projectApi.createProject(group.getId(), projectName);
                            if (AssertNP.legalParams(project)) {
                                return true;
                            }
                        }
                    }
                }
            }
        } catch (GitLabApiException e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        return false;
    }


    /**
     * @param projectName  myapp1
     * @param preGroupName helmroot
     * @return
     */
    public Project createProject(String projectName, String preGroupName) {
        ProjectApi projectApi = gitLabApi.getProjectApi();
        Integer id = getGroupIdByName(preGroupName);
        if (AssertNP.legalParams(id)) {
            try {
                return projectApi.createProject(id, projectName);
            } catch (GitLabApiException e) {
                log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
            }
        }
        return null;
    }


    /**
     * @param projectName  myapp1
     * @param projectPath  myapp1
     * @param preGroupName helmroot
     * @return
     */
    public Project createProject(String projectName, String projectPath, String preGroupName) {
        ProjectApi projectApi = gitLabApi.getProjectApi();
        //
        Project project = new Project();
        project.setName(projectName);
        project.setPath(projectPath);
        //
        Integer id = getGroupIdByName(preGroupName);
        if (AssertNP.legalParams(id)) {
            try {
                return projectApi.createProject(id, project);
            } catch (GitLabApiException e) {
                log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
            }
        }
        return null;
    }


    /**
     * @param search the group name or path search criteria
     * @return
     */
    public List<Group> getGroups(String search) {
        GroupApi groupApi = gitLabApi.getGroupApi();
        try {
            if (AssertNP.legalParams(search)) {
                return groupApi.getGroups(search);
            } else {
                return groupApi.getGroups();
            }
        } catch (GitLabApiException e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        return null;
    }


    public Group getGroup(Object groupIdOrPath) {
        GroupApi groupApi = gitLabApi.getGroupApi();
        try {
            return groupApi.getGroup(groupIdOrPath);
        } catch (GitLabApiException e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        return null;
    }


    /**
     * @param groupName 组全名
     * @return
     */
    public Integer getGroupIdByName(String groupName) {
        GroupApi groupApi = gitLabApi.getGroupApi();
        List<Group> groups = null;
        try {
            groups = groupApi.getGroups(groupName);
        } catch (GitLabApiException e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        if (AssertNP.legalParams(groups)) {
            for (Group group : groups) {
                if (AssertNP.legalParams(group)) {
                    if (AssertNP.equals(groupName, group.getName())) {
                        return group.getId();
                    }
                }
            }
        }
        return null;
    }


    /**
     * @param name helmroot
     * @param path helmroot
     * @return
     */
    public Group addRootGroup(String name, String path) {
        GroupApi groupApi = gitLabApi.getGroupApi();
        try {
            return groupApi.addGroup(name, path);
        } catch (GitLabApiException e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        return null;
    }


    /**
     * @param preGroupName helmgroup
     * @param newGroupName demo
     * @param newGroupPath demo
     * @param newGroupDesc demo
     * @return
     */
    public Group addGroupInGroup(String preGroupName, String newGroupName, String newGroupPath, String newGroupDesc) {
        GroupApi groupApi = gitLabApi.getGroupApi();
        List<Group> groups = getGroups(preGroupName);
        if (AssertNP.legalParams(groups)) {
            for (Group group : groups) {
                if (AssertNP.legalParams(group)) {
                    if (AssertNP.equals(group.getName(), preGroupName)) {
                        Group group1 = new Group();
                        group1.setName(newGroupName);
                        group1.setPath(newGroupPath);
                        group1.setDescription(newGroupDesc);
                        group1.setParentId(group.getId());
                        group1.setVisibility(Visibility.PUBLIC);
                        //
                        try {
                            return groupApi.addGroup(group1);
                        } catch (GitLabApiException e) {
                            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
                        }
                    }
                }
            }
        }
        return null;
    }


    /**
     * @param preGroupName         appstore
     * @param newGroupName         demo
     * @param newGroupPath         demo
     * @param newGroupDesc         demo
     * @param lfsEnabled           true
     * @param requestAccessEnabled true
     * @return
     */
    public Group addGroupInGroup(String preGroupName, String newGroupName, String newGroupPath, String newGroupDesc, boolean lfsEnabled, boolean requestAccessEnabled) {
        GroupApi groupApi = gitLabApi.getGroupApi();
        Integer id = getGroupIdByName(preGroupName);
        if (AssertNP.legalParams(id)) {
            try {
                return groupApi.addGroup(newGroupName, newGroupPath, newGroupDesc, Visibility.PUBLIC, lfsEnabled, requestAccessEnabled, id);
            } catch (GitLabApiException e) {
                log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
            }
        }
        return null;
    }


    /**
     * 上传单个文件到指定目录, 替换或新增文件
     *
     * @param fileDir
     * @return
     */
    public boolean updateFile(FileDir fileDir) {
        //
        if (AssertNP.legalParams(fileDir)) {
            //文件夹
            String rootGroup = fileDir.getRootGroup();
            String gitBaseUrl = fileDir.getGitBaseUrl();
            //文件
            String fileDirType = fileDir.getType();
            String projectName = fileDir.getProjectName();
            String filePath = fileDir.getFilePath();
            String fileDirName = fileDir.getFileName();
            String fileDirContent = fileDir.getFileContent();
            String commitMsg = fileDir.getCommitMsg();
            String branchName = fileDir.getBranchName();
            //
            if (AssertNP.legalParams(fileDirType, fileDirName, fileDirContent, branchName)) {
                boolean flag = fileToGit3Step(projectName, filePath, fileDirName, fileDirContent, branchName, commitMsg);
                log.info("\nfileToGit=============>" + flag);
                return flag;
            }
        }
        return false;
    }


    /**
     * 上传文件三部曲
     *
     * @param projectName
     * @param filePath
     * @param fileDirName
     * @param fileDirContent
     * @param branchName
     * @param commitMsg
     * @return
     */
    private boolean fileToGit3Step(String projectName, String filePath, String fileDirName, String fileDirContent, String branchName, String commitMsg) {
        //String filePath = getPathWithListString(getNameList(logicApp, env, argoApp, fileDirName));
        //String filePath = logicApp + "/" + env + "/" + argoApp + "/" + fileDirName;
        boolean flag1 = fileIsExist(projectName, filePath, branchName);
        boolean flag2 = false;
        boolean flag3 = false;
        if (flag1) {
            //存在, 更新
            flag3 = updateFile(projectName, filePath, fileDirName, fileDirContent, branchName, commitMsg);
            log.info("\n=========>" + flag3);
        } else {
            //不存在, 上传
            flag2 = uploadFile(projectName, filePath, fileDirName, fileDirContent, commitMsg, branchName);
            log.info("\n=========>" + flag2);
        }
        return AssertNP.atLeastOneTrue(flag2, flag3);
    }

    /**
     * @param args
     * @return
     */
    public List<String> getNameList(String... args) {
        List<String> list = new ArrayList<>();
        if (AssertNP.legalParams(args)) {
            for (String arg : args) {
                list.add(arg);
            }
        }
        return list;
    }


    /**
     * @param nameList
     * @return
     */
    public String getPathWithListString(List<String> nameList) {
        StringBuilder builder = new StringBuilder();
        if (AssertNP.legalParams(nameList)) {
//            for (String name : nameList) {
//                if (name.contains(".")) {
//                    builder.append(name);
//                } else {
//                    builder.append(name + "/");
//                }
//            }
            for (int i = 0; i < nameList.size(); i++) {
                if (i == nameList.size() - 1) {
                    builder.append(nameList.get(i));
                } else if (nameList.get(i).contains(".")) {
                    builder.append(nameList.get(i));
                } else {
                    builder.append(nameList.get(i) + "/");
                }
            }
            return builder.toString();
        }
        return null;
    }


//    public static void main(String[] args) {
//
//        List<String> listString = getListString("aaa", "bbb", "ccc", "d.txt");
//        System.out.println("listString =\n" + listString);
//
//        String pathWithListString = getPathWithListString(listString);
//        System.out.println("pathWithListString =\n" + pathWithListString);//  aaa/bbb/ccc/d.txt
//
//    }

    /**
     * file1 =
     * {
     * "fileName" : null,
     * "filePath" : "abc/efg/d.txt",
     * "size" : null,
     * "encoding" : null,
     * "content" : null,
     * "contentSha256" : null,
     * "ref" : null,
     * "blobId" : null,
     * "commitId" : null,
     * "lastCommitId" : null
     * }
     * <p>
     * myapp1系统名, 既系统的git项目
     * myapp1/charts/microservice/templates/service.yaml
     * <p>
     * 上传已存在的文件会报错:  A file with this name already exists
     *
     * @param projectName 项目名 要求全名, 且唯一
     * @param filePath    文件路径     myapp1/a.txt  或  servicePath = "charts/microservice/templates/service.yaml";// microservice.yaml
     *                    file_path should be a valid file path   /abc/d.txt项目下的路径不能用根号打头 ==> abc/d.txt是合理的
     *                    可以自动创建目录,文件, 且不会清除原有路径体系下的文件/夹及内容
     * @param fileName    文件名
     * @param fileContent 文件内容,或编码
     * @param commitMsg   提交信息
     * @param branchName  分支名
     * @return
     */
    public boolean uploadFile(String projectName, String filePath, String fileName, String fileContent, String commitMsg, String branchName) {
        try {
            ProjectApi projectApi = gitLabApi.getProjectApi();
            List<Project> projects = projectApi.getProjects(projectName);
            if (AssertNP.legalParams(projects)) {
                for (Project project : projects) {
                    if (AssertNP.equals(projectName, project.getName())) {
                        Integer projectId = project.getId();
                        //
                        RepositoryFile repositoryFile = new RepositoryFile();
                        repositoryFile.setFileName(fileName);
                        repositoryFile.setFilePath(filePath);
                        repositoryFile.setContent(fileContent);
                        //
                        RepositoryFileApi fileApi = gitLabApi.getRepositoryFileApi();
                        RepositoryFile file = fileApi.createFile(projectId, repositoryFile, branchName, commitMsg);
                        if (AssertNP.legalParams(file)) {
                            return true;
                        }
                    }
                }
            }
        } catch (GitLabApiException e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        return false;
    }


    /**
     * @param projectName 项目全名, 要求唯一
     * @return
     */
    public Integer getProjectIdByName(String projectName) {
        try {
            ProjectApi projectApi = gitLabApi.getProjectApi();
            List<Project> projects = projectApi.getProjects(projectName);
            if (AssertNP.legalParams(projects)) {
                for (Project project : projects) {
                    if (AssertNP.equals(projectName, project.getName())) {
                        return project.getId();
                    }
                }
            }
        } catch (GitLabApiException e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        return null;
    }


    /**
     * @param projectName myapp1
     * @param filePath    abc/efg/d.txt
     * @param fileName    d.txt
     * @param fileContent dadada
     * @param branchName  master
     * @param commitMsg   dadada
     * @return
     */
    public boolean updateFile(String projectName, String filePath, String fileName, String fileContent, String branchName, String commitMsg) {
        Integer id = getProjectIdByName(projectName);
        if (AssertNP.legalParams(id)) {
            RepositoryFileApi fileApi = gitLabApi.getRepositoryFileApi();
            RepositoryFile file = new RepositoryFile();
            file.setFilePath(filePath);
            file.setFileName(fileName);
            file.setContent(fileContent);
            //
            RepositoryFile file1 = null;
            try {
                file1 = fileApi.updateFile(id, file, branchName, commitMsg);
            } catch (GitLabApiException e) {
                log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
            }
            if (AssertNP.legalParams(file1)) {
                return true;
            }
        }
        return false;
    }


    /**
     * @param projectName myapp1
     * @param filePath    abc/efg/e.txt
     * @param branchName  master
     * @param commitMsg   delFile
     * @return
     */
    public boolean delFile(String projectName, String filePath, String branchName, String commitMsg) {
        Integer id = getProjectIdByName(projectName);
        if (AssertNP.legalParams(id)) {
            RepositoryFileApi fileApi = gitLabApi.getRepositoryFileApi();
            try {
                //系统项目.git  逻辑态应用/环境/运行态应用/...
                fileApi.deleteFile(id, filePath, branchName, commitMsg);
                return true;
            } catch (GitLabApiException e) {
                log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
            }
        }
        return false;
    }


    /**
     * @param projectName myapp1
     * @param filePath    abc/efg/d.txt
     * @param branchName  master
     * @return RepositoryFile 需要解码, 上传直接原文上传
     */
    public RepositoryFile getFile(String projectName, String filePath, String branchName) {
        Integer id = getProjectIdByName(projectName);
        if (AssertNP.legalParams(id)) {
            RepositoryFileApi fileApi = gitLabApi.getRepositoryFileApi();
            RepositoryFile file = null;
            try {
                file = fileApi.getFile(id, filePath, branchName);
            } catch (GitLabApiException e) {
                log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
            }
            if (AssertNP.legalParams(file)) {
                return file;
            }
        }
        return null;
    }


    /**
     * @param projectName myapp1
     * @param filePath    abc/efg/e.txt
     * @param branchName  master
     * @return
     */
    public boolean fileIsExist(String projectName, String filePath, String branchName) {
        RepositoryFile file = getFile(projectName, filePath, branchName);
        if (AssertNP.legalParams(file)) {
            return true;
        }
        return false;
    }


    /**
     * @param base64Content
     * @return
     */
    public String base64Decode(String base64Content) {
        //cn.hutool.core.codec.Base64.decode
        byte[] decode = decode(base64Content);
        return new String(decode);
    }


    /**
     * @param file RepositoryFile
     * @return
     */
    public String base64Decode(RepositoryFile file) {
        if (AssertNP.legalParams(file)) {
            //不依赖第三方库
            return file.getDecodedContentAsString();
        }
        return null;
    }


    /**
     * @return
     */
    public List<User> getUserList() {
        UserApi userApi = gitLabApi.getUserApi();
        List<User> users = null;
        try {
            users = userApi.getUsers();
        } catch (GitLabApiException e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        if (AssertNP.legalParams(users)) {
            return users;
        }
        return null;
    }


    /**
     * name: 张广成
     *
     * @param username zhangguangcheng
     * @return
     */
    public Integer getUserIdByUsername(String username) {
        List<User> userList = getUserList();
        if (AssertNP.legalParams(userList)) {
            for (User user : userList) {
                if (AssertNP.equals(username, user.getUsername())) {
                    return user.getId();
                }
            }
        }
        return null;
    }


    /**
     * 仅管理员可用
     *
     * @param userId
     * @return
     */
    public boolean blockUser(Integer userId) {
        UserApi userApi = gitLabApi.getUserApi();
        try {
            userApi.blockUser(userId);
            return true;
        } catch (GitLabApiException e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        return false;
    }


    /**
     * 仅管理员可用
     *
     * @param userId
     * @return
     */
    public boolean unblockUser(Integer userId) {
        UserApi userApi = gitLabApi.getUserApi();
        try {
            userApi.unblockUser(userId);
        } catch (GitLabApiException e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        return false;
    }


    /**
     * @param userIdOrUsername
     * @param impersonationName
     * @param expiresAt
     * @param scopes
     * @return
     */
    public ImpersonationToken createOneImpersonationToken(Object userIdOrUsername, String impersonationName, Date expiresAt, ImpersonationToken.Scope[] scopes) {
        UserApi userApi = gitLabApi.getUserApi();
        try {
            return userApi.createImpersonationToken(userIdOrUsername, impersonationName, expiresAt, scopes);
        } catch (Exception e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        return null;
    }


    /**
     * @param userIdOrUsername
     * @param tokenId
     * @return
     */
    public boolean delOneImpersonationToken(Object userIdOrUsername, Integer tokenId) {
        UserApi userApi = gitLabApi.getUserApi();
        try {
            userApi.revokeImpersonationToken(userIdOrUsername, tokenId);
            return true;
        } catch (GitLabApiException e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        return false;
    }


    /**
     * @param name
     * @param userName
     * @param email
     * @param password
     * @return
     */
    public User addUser(String name, String userName, String email, String password) {
        UserApi userApi = gitLabApi.getUserApi();
        //
        User user = new User();
        user.setName(name);
        user.setUsername(userName);
        user.setEmail(email);
        //
        CharSequence pwd = password;
        //
        try {
            return userApi.createUser(user, pwd, false);
        } catch (GitLabApiException e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        return null;
    }


    /**
     * @param projectIdOrPath
     * @return
     */
    public List<Commit> getCommits(Object projectIdOrPath) {
        CommitsApi commitsApi = gitLabApi.getCommitsApi();
        try {
            return commitsApi.getCommits(projectIdOrPath);
        } catch (GitLabApiException e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        return null;
    }


    /**
     * @param projectIdOrPath
     * @param commitId        a commit hash or name of a branch or tag
     * @return
     */
    public List<Diff> getDiff(Object projectIdOrPath, String commitId) {
        CommitsApi commitsApi = gitLabApi.getCommitsApi();
        try {
            return commitsApi.getDiff(projectIdOrPath, commitId);
        } catch (GitLabApiException e) {
            log.info("[**======> {} **]", e.getMessage() + " >> " + e.getStackTrace()[0] + " >> " + e.getCause());
        }
        return null;
    }


}

